#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#define _CRT_SECURE_NO_WARNINGS 

#include <iostream>
#include <string>
#include <vector>
#include <time.h>
using namespace std;
#include <winsock2.h>
#include <regex>
#pragma comment(lib,"ws2_32.lib")

class Exception {
	string msg;
public:
	Exception(string s) {
		msg = s;
	}
	void Print() {
		cout << "Exception: " << msg << endl;
	}
};
class TCPConnector {
	SOCKET sock;
public:
	TCPConnector(string destIP, int port) {
		try {
			sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
			if (sock == INVALID_SOCKET) throw 2;
			sockaddr_in sockad;
			sockad.sin_family = AF_INET;
			sockad.sin_addr.S_un.S_addr = inet_addr(destIP.c_str());
			sockad.sin_port = htons(port);
			if (connect(sock, (sockaddr*)(&sockad), sizeof(sockad)) == SOCKET_ERROR)
				throw 3;
		}
		catch (int e) {
			if (e == 2)
				if (e == 3) {
					int iResult = closesocket(sock);
					if (iResult == SOCKET_ERROR)
						wprintf(L"closesocket function failed with error: %d\n", WSAGetLastError());
				}
			printf("Error Code:%d\n", WSAGetLastError());
			throw Exception("create TCP connection failed");
		}
	}
	bool Send(const string & s) {
		const char* buf = s.c_str();
		int cnt = s.length() / 7000;
		int i = 0;
		int pd = cnt / 50 + 1;
		for (; i < cnt; i++) {
			if (send(sock, buf + i * 7000, 7000, 0) == SOCKET_ERROR) { return false; }
			if (i % pd == 0) printf("#");

		}
		if (s.length() % 7000 != 0) {
			if (send(sock, buf + i * 7000, s.length() % 7000, 0) == SOCKET_ERROR) {
				return false;
			}
		}
		return true;
	}
	string Receive(int charLength = -1, string delimit = "\r\n.\r\n") { // charLength >= len		
		if (charLength < 0) {
			char* buf = new char[7005];
			string s = "";
			int len;
			int i = 0;
			while ((len = recv(sock, buf, 7000, 0)) != SOCKET_ERROR) {
				buf[len] = '\0';
				i++;
				if (i == 50) {
					printf("#");
					i = 0;
				}
				string t = buf;
				s += t;
				if (s.substr(s.length() - 5) == delimit) break;
			}
			delete [] buf;
			return s;
		}
		else {
			char* buf = new char[charLength + 5];
			string s;
			int len;
			if ((len = recv(sock, buf, charLength, 0)) != SOCKET_ERROR) {
				buf[len] = '\0';
				s = buf;
				delete [] buf;
				return s;
			}
		}
	}
	~TCPConnector() {
		int iResult = closesocket(sock);
		if (iResult == SOCKET_ERROR)
			wprintf(L"closesocket function failed with error: %d\n", WSAGetLastError());
	}
};
string GetEmailServerIP(string protoName, string emailAddress) {
	string serverName = protoName + "." + emailAddress.substr(emailAddress.find("@") + 1);
	HOSTENT* hostent = gethostbyname(serverName.c_str());
	if (hostent == NULL) throw Exception("gethostbyname failed with " + serverName);
	if (hostent->h_addrtype != AF_INET) throw Exception("hostent addrtype is not AF_INET");
	in_addr addr;
	if (hostent->h_addr_list[0] == 0) throw Exception("hostent addr_list[0] equals zero");
	addr.S_un.S_addr = *(u_long*)hostent->h_addr_list[0];
	return inet_ntoa(addr);
}
string GetUserFromEmailAddress(string emailAddress) {
	return emailAddress.substr(0, emailAddress.find("@"));
}
//+OK 20 72782
void SplitString(vector<string>& res, string s, char c) {
	if (s.length() > 0) {
		int start = 0;
		if (s[0] == c) start = 1;
		for (int i = start + 1; i < s.length(); i++) {
			if (s[i] == c) {
				res.push_back(s.substr(start, i - start));
				start = i + 1;
			}
		}
		if (start != s.length()) res.push_back(s.substr(start));
	}
}
//输出的字符如果超过76个字符就会自动换行
bool EnCode(string& res, string filename, string rule) {
	FILE* fp = fopen(filename.c_str(), "rb");
	if (fp == NULL) return false;
	fseek(fp, 0, SEEK_END);
	int len = ftell(fp);
	char* out = new char[(int)(1.5 * len)];
	memset(out, 0, len);
	fseek(fp, 0, SEEK_SET);
	unsigned char in[3];
	memset(in, 0, 3);
	int i = 0;
	string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	int j = 0;
	int c = 0;
	while ((j = fread(in, 1, 3, fp)) == 3) {
		out[i] = a[(char)(in[0] >> 2)];
		out[i + 1] = a[(char)((in[0] & 0x03) << 4 | in[1] >> 4)];
		out[i + 2] = a[(char)((in[1] & 0x0f) << 2 | in[2] >> 6)];
		out[i + 3] = a[(char)(in[2] & 0x3f)];
		i += 4;
		memset(in, 0, 3);

		c += 4;
		if (c == 76) {
			out[i] = '\r';
			out[i + 1] = '\n';
			i += 2;
			c = 0;
		}

	}
	if (j != 0) {
		out[i] = a[(char)(in[0] >> 2)];
		out[i + 1] = a[(char)((in[0] & 0x03) << 4 | in[1] >> 4)];
		out[i + 2] = a[(char)((in[1] & 0x0f) << 2 | in[2] >> 6)];
		out[i + 3] = a[(char)(in[2] & 0x3f)];
		if (j == 2) out[i + 3] = '=';
		if (j == 1) {
			out[i + 3] = '=';
			out[i + 2] = '=';
		}
		out[i + 4] = '\0';
	}
	else out[i] = '\0';
	res = out;
	fclose(fp);
	return true;
}
bool DeCode(string filename, const string& src, string rule) {
	FILE* fp = fopen(filename.c_str(), "wb");
	if (fp == NULL) {
		printf("can't open %s\n", filename.c_str());
		return false;
	}
	char a[127];
	for (int i = 'a', j = 'A', k = 0; i <= 'z'; i++, j++, k++) {
		a[i] = k + 26;
		a[j] = k;
	}
	for (int i = '0', j = 52; i <= '9'; i++, j++) a[i] = j;
	a['+'] = 62;
	a['/'] = 63;
	a['='] = 0;
	int len = src.length();
	int i = 3;
	unsigned char b[4];
	char buf[3];
	for (; i < len; i += 4) {
		b[0] = a[src[i - 3]];
		b[1] = a[src[i - 2]];
		b[2] = a[src[i - 1]];
		b[3] = a[src[i]];

		buf[0] = (b[0] << 2) | (b[1] >> 4);
		buf[1] = (b[1] << 4) | (b[2] >> 2);
		buf[2] = (b[2] << 6) | b[3];
		if (src[i] != '=') fwrite(buf, 1, 3, fp);
		else {
			if (src[i] == '=' && src[i - 1] == '=') fwrite(buf, 1, 1, fp);
			else if (src[i] == '=') fwrite(buf, 1, 2, fp);
		}
	}
	fclose(fp);
	return true;
}
void MyBoundaryFind(string src, string boundary, vector<int>& index) {
	int len = boundary.length();
	int pre = src.find(boundary);
	src = src.substr(pre + len);
	index.push_back(pre);
	int i;
	while ((i = src.find(boundary)) != string::npos) {
		pre = pre + len + i;
		index.push_back(pre);
		src = src.substr(i + len);
	}
}
string GetEnCodeContent(const string& s, string encoding = "base64") {
	smatch sm;
	regex_search(s,sm,regex("(\\r?\\n)\\1"));
	int i = sm.position();
	if (s[i] == '\r') i = i + 4;
	else i = i + 3;
	if (encoding != "base64") return s.substr(i);
	int slen = s.length();

	int k = i;
	for (; s[k] != '\r'; k++);
	int linelen = k - i;
	string ret = "";
	for (; i < slen; i += linelen + 2) {
		if (i + linelen > slen) {
			int j = i;
			for (; s[j] != '\r' && s[j] != '\n'; j++);
			ret += s.substr(i, j - i);
		}
		else ret += s.substr(i, linelen);
	}
	return ret;
}
bool DeQuoPri(const string& _src, string filename) {
	FILE* fp = fopen(filename.c_str(), "wb");
	if (fp) {
		int len = _src.length();
		unsigned char* buffer = new unsigned char[len];
		char* src = new char[len];
		memcpy(src, _src.c_str(), len);
		int b = 0;
		int pt = 0;
		auto f = [](char c) ->int { return c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - '0'; };
		auto isUpperAlnum = [](char a) {return (a >= 'A' && a <= 'F') || (a >= '0' && a <= '9') ? true : false; };
		for (int i = 0; i < len;) {
			if (src[i] == '=' && src[i + 1] == '\n') {
				memcpy(buffer + pt, src + b, i - b);
				pt = pt + i - b;
				b = i = i + 2;
				continue;
			}
			if (src[i] == '=' && src[i + 1] == '\r' && src[i + 2] == '\n') {
				memcpy(buffer + pt, src + b, i - b);
				pt = pt + i - b;
				b = i = i + 3;
				continue;
			}
			if (src[i] == '=' && isUpperAlnum(src[i + 1]) && isUpperAlnum(src[i + 2])) {
				memcpy(buffer + pt, src + b, i - b);
				pt = pt + i - b;
				unsigned char n = 0;
				n = f(src[i + 1]) * 16 + f(src[i + 2]);
				buffer[pt] = n;
				pt = pt + 1;
				b = i = i + 3;
				continue;
			}
			i++;
		}
		if (b < len) {
			memcpy(buffer + pt, src + b, len - b);
			pt = pt + len - b;
		}
		fwrite(buffer, 1, pt, fp);
		delete[] buffer;
		delete[] src;
		fclose(fp);
		return true;
	}
	else return false;
}
string GetStringFromFile(string charset) {
	string ret = "";
	if ( charset=="utf-8") {
		//temp code by utf-8	
		FILE* fp = _wfopen(L"temp", L"r,ccs=utf-8");
		wchar_t buf[150];
		if (fp != NULL) fwscanf(fp, L"%s", buf);
		fclose(fp);
		char mbBuf[300];
		WideCharToMultiByte(CP_ACP, 0, buf, -1, mbBuf, 300, "#", 0);
		ret = string(mbBuf);
	}
	else {
		FILE* fp = fopen("temp", "rb");
		char buf[300];
		if (fp) {
			fseek(fp, 0, SEEK_END);
			int size = ftell(fp);
			fseek(fp, 0, SEEK_SET);
			fread(buf, 1, size, fp);
			buf[size] = '\0';
		}
		fclose(fp);
		ret = string(buf);
	}
	return ret;
}
//Subject: =?UTF-8?B?572R5piT6YKu566x77yM6YKj5Lqb5L2g?=
// =?UTF-8?B?55+l6YGT5ZKM5LiN55+l6YGT55qE5LqL?=

//"=?UTF-8?B?5LqU6YKR5aSn5a2m572R57uc5a6J5YWo5pyf?=
// =?UTF-8?B?5pyr5aSN5Lmg6aKYKDEpLmRvY3g=?="

//filename*=utf-8''%E6%9D%A8zi.jpg
//=?utf-8?B?5oeC5rOV5a6I5rOV55yL5pe26Ze0?=
//=?gb18030?B?zrTD/MP7My5jcHA=?=
//20200610145415zecyab.jpg
//From: "=?gb18030?B?sfnQxMrExsazpNK5zOw=?=" <xjqxz4yth@qq.com>
//To: "=?gb18030?B?MTU4MTk4MjcyMTI=?=" <15819827212@189.cn>
string ParseString(string s) {
	string ret = "";
	if (s.length() > 0) {
		string out = regex_replace(s, regex("[\"\\r\\n\\t]"), " ");
		out = regex_replace(out, regex("(\\s)\\1+"), " ");
		vector<string> res;
		SplitString(res, out, ' ');
		for (int i = 0; i < res.size(); i++) {
			if (res[i].size() > 0 && res[i][0] == '=') {
				vector<string> v;
				SplitString(v, res[i], '?');
				if (v.size() > 1) {
					if (v[2][0] == 'B' || v[2][0] == 'b') {
						if (v[1][0] == 'U' || v[1][0] == 'u') {
							DeCode("temp", v[3], "base64");
							ret += GetStringFromFile("utf-8");
						}
						else {
							DeCode("temp", v[3], "base64");
							ret += GetStringFromFile("ansi");
						}
					}
					else {
						if (v[1][0] == 'U' || v[1][0] == 'u') {
							DeQuoPri(v[3],"temp");
							ret += GetStringFromFile("utf-8");
						}
						else {
							DeQuoPri(v[3], "temp");
							ret += GetStringFromFile("ansi");
						}
					}
				}
				else ret += res[i];
			}
			else ret += res[i];
		}
	}
	return ret;
}
string GetValueByKey(string s, string key) {
	int k = s.find(key);
	if (k == string::npos) return "";
	for (; s[k] != ':'; k++);
	k = k + 1;
	smatch sm;
	regex_search(s.cbegin() + k, s.cend(), sm, regex("\\r?\\n[\\w\\r\\n]"));
	return s.substr(k, sm.position());
}
string GetFieldInValue(string value, string f) {
	string ret = "";
	if (value.find(f) != string::npos) {
		int s = value.find(f) + f.length() + 1; // s 指向'=' '/' '*' 后的第一个字符
		int e = s;
		for (; e < value.length() && value[e] != ';'; e++);
		ret = value.substr(s, e - s);
	}
	return ret;
}
class MIMEContent {
	vector<MIMEContent*> items;
	bool isMultipart;
	string boundary;  //not NULL represents multipart  
	string contentType;
	string contentTransferEnCodeing;
	string contentDisposition;
	string contentString;
private:

public:
	MIMEContent(string contentType, string contentTransferEnCodeing,
		string contentFileName = "", string contentDisposition = "") {
		//Content-Type: text/plain;
		//charset="gb18030"
		//Content-Transfer-Encoding: base64

		//Content-Type: image/jpeg;
		//  name="mmexport1590651798523.jpg"
		//Content-Transfer-Encoding: base64
		//Content-Disposition: attachment;
		//  filename="mmexport1590651798523.jpg";
		this->contentType = contentType;
		this->contentTransferEnCodeing = contentTransferEnCodeing;
		this->contentDisposition = contentDisposition;
		this->contentString = "";

		if (contentFileName == "") {
			this->isMultipart = true;
			char buf[50];
			sprintf(buf, "----=_MYEMAIL_Part_%d\0", time(NULL));
			this->contentType = this->contentType + "\r\n\tboundary=\"" + string(buf) + "\"";
			this->boundary = ("--") + string(buf);
		}
		else if (contentDisposition == "") {
			//content
			this->isMultipart = false;
			EnCode(this->contentString, contentFileName, this->contentTransferEnCodeing);
		}
		else {
			//attachment
			this->isMultipart = false;
			//--------
			FILE* fp = fopen("temp", "w");
			fprintf(fp, "%s", contentFileName.c_str());
			fclose(fp);
			string res;
			EnCode(res, "temp", "base64");
			string t = "=?gb18030?B?" + res + "?=";
			//--------
			this->contentDisposition += ("filename=\"" + t + "\";");
			EnCode(this->contentString, contentFileName, this->contentTransferEnCodeing);
		}

	}
	MIMEContent(const string & MIMEString) {
		this->contentType = GetValueByKey(MIMEString, "Content-Type");
		if (this->contentType.find("multipart") == string::npos &&
			this->contentType.find("Multipart") == string::npos) {
			this->contentTransferEnCodeing = GetValueByKey(MIMEString, "Content-Transfer-Encoding");
			this->contentDisposition = GetValueByKey(MIMEString, "Content-Disposition");
			if (contentTransferEnCodeing.find("quoted-printable") != string::npos )
				this->contentString = GetEnCodeContent(MIMEString, "others"); // \r\n\r\n
			else this->contentString = GetEnCodeContent(MIMEString);
			this->isMultipart = false;
		}
		else {
			this->isMultipart = true;
			this->boundary = regex_replace(GetFieldInValue(contentType, "boundary"), regex("\""), "");
			this->contentTransferEnCodeing = GetValueByKey(MIMEString, "Content-Transfer-Encoding");

			vector<int> index;
			MyBoundaryFind(MIMEString, "--" + boundary, index);
			int count = index.size() - 1;
			for (int i = 0; i < count; i++) {
				items.push_back(new MIMEContent(MIMEString.substr(index[i], index[i + 1] - index[i])));
			}
		}
	}
	void SaveToFile(string subject) {
		if (!isMultipart) {
			string filename = GetFieldInValue(contentType, "name");
			filename = ParseString(filename);
			if (filename == "") filename = ParseString(GetFieldInValue(contentDisposition, "filename"));
			//如果在 Content-Type找到name字段 或者 Content-Disposition中找到filename字段，
			//则按照给出的名字命名文件
			if (filename == "") {// 是一个邮件内容
				filename = subject;
				string t = GetFieldInValue(contentType, "text");
				if (t == "") t = GetFieldInValue(contentType, "Text");
				if (t == "") t = "无法识别类型";
				filename += ("." + t);
			}
			if (contentTransferEnCodeing.find("base64") != string::npos
				|| contentTransferEnCodeing.find("Base64") != string::npos) {
				DeCode(filename, contentString, "base64");
				printf("had saved %s\n", filename.c_str());
			}
			else {//不是base64，都认为是QuoPri
				DeQuoPri(contentString, filename);
				printf("had saved %s\n",filename.c_str());
			}
		}
		else {
			for (unsigned int i = 0; i < items.size(); i++) {
				items[i]->SaveToFile(subject);
			}
		}
	}
	void AddMIMEContent(MIMEContent* c) {
		items.push_back(c);
	}
	string ToString() {
		string s;
		if (!isMultipart) {
			string add = "";
			if (contentDisposition != "") add = "\r\nContent-Disposition: " + contentDisposition;
			s = "\r\nContent-Type: " + contentType + "\r\n" + "Content-Transfer-Encoding: " +
				contentTransferEnCodeing + add + "\r\n\r\n"
				+ contentString + "\r\n";

		}
		else {
			s = "Content-Type: " + contentType + "\r\n" + "Content-Transfer-Encoding: "
				+ contentTransferEnCodeing + "\r\n\r\n";
			for (int i = 0; i < items.size(); i++) {
				s += boundary;
				s += items[i]->ToString();
			}
			s += boundary + "--\r\n";
		}
		return s;
	}
};
string GetLocalCST() {
	time_t rawtime;
	time(&rawtime);
	struct tm* info = localtime(&rawtime);
	//Wed, 10 Jun 2020 14:55:15 +0800 (CST)
	string week[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
	string month[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec" };
	char buf[50];
	sprintf(buf, "%s, %d %s %d %02d:%02d:%02d\0", week[info->tm_wday].c_str(),
		info->tm_mday, month[info->tm_mon].c_str(), info->tm_year + 1900,
		info->tm_hour, info->tm_min, info->tm_sec);
	return string(buf) + " +0800 (CST)";

}
class Email {
	string topStr;
	string allStr;
	string from;
	string to;
	string date;
	string subject;
	MIMEContent* content;
public:
	Email() {
		content = NULL;
	}
	Email& FillTopInfo(const string & s) {
		this->topStr = s;
		this->from = ParseString(GetValueByKey(s, "\nFrom"));
		this->to = ParseString(GetValueByKey(s, "\nTo"));
		this->date = GetValueByKey(s, "\nDate");
		this->subject = ParseString(GetValueByKey(s, "\nSubject"));
		string t = "\\/*:?\"<>|";
		for (int i = 0; i < subject.length(); i++) {
			if (t.find(subject[i]) != string::npos) subject[i] = '_';
		}
		return *this;
	}
	Email& PrintTopInfo() {
		//printf("ID\tFrom\tTo\tDate\tSubject\tCount=%d\n",emailCount);
		printf("%s\t%s\t%s\t%s\n", from.c_str(), to.c_str(), date.c_str(), subject.c_str());
		return *this;
	}
	Email& FillAll(const string & s) {
		this->allStr = s;
		FillTopInfo(s);
		return *this;
	}
	Email& Save() {
		content = new MIMEContent(allStr.substr(allStr.find("\nContent-Type:")));
		content->SaveToFile(this->subject);
		return *this;
	}
	Email& SetFrom(string srcEmailAddress) {
		this->from = srcEmailAddress;
		return *this;

	}
	Email& SetTo(string destEmailAddress) {
		this->to = destEmailAddress;
		return *this;
	}
	Email& SetSubject(string s) {
		//=?gb18030?B?zrTD/MP7My5jcHA=?=
		FILE* fp = fopen("temp", "w");
		fprintf(fp, "%s", s.c_str());
		fclose(fp);
		string res;
		EnCode(res, "temp", "base64");
		this->subject = "=?gb18030?B?" + res + "?=";
		return *this;
	}
	Email& SetMIMEContent(MIMEContent* cont) {
		this->content = cont;
		return *this;
	}
	string BuidEmailString() {
		string s = "";
		s = "MIME-Version: 1.0\r\nFrom: " + from + "\r\nTo: " + to + "\r\nSubject: " + subject + "\r\nDate: " + GetLocalCST() + "\r\n"
			+ content->ToString() + "\r\n.\r\n";
		return s;
	}

};
class POP3Connector : public TCPConnector {
	string emailAddress;
	string password;
	static const int port = 110;

public:
	POP3Connector(string emailAddress, string password) :TCPConnector(GetEmailServerIP("pop", emailAddress), port) {
		this->emailAddress = emailAddress;
		this->password = password;
		string s = "user " + GetUserFromEmailAddress(emailAddress) + "\r\n";
		Receive(2000);   //
		if (Send(s)) {
			string ret = Receive(2000);
			if (ret.length() > 0 && ret[0] == '+') {
				s = "pass " + password + "\r\n";
				if (Send(s)) {
					ret = Receive(2000);
					if (ret.length() <= 0 || ret[0] != '+') throw Exception(ret);
				}
				else throw Exception("Send failed with CML " + s);
			}
			else throw Exception(ret);
		}
		else throw Exception("Send failed with CML " + s);
	}
	void List(int count = -1) {
		int emailCount = 0;
		if (Send("stat\r\n")) {
			string ret;
			ret = Receive(100);
			if (ret.length() > 0 && ret[0] == '+') {
				vector<string> res;
				SplitString(res, ret, ' ');
				sscanf(res[1].c_str(), "%d\n", &emailCount);
				printf("ID\tFrom\tTo\tDate\tSubject\tCount=%d\n", emailCount);
				if (count == -1) count = emailCount;
				if (count > emailCount) count = emailCount;
				for (int i = emailCount; i > emailCount - count; i--) {
					char buf[20];
					sprintf(buf, "%d", i);
					printf("%d\t", i);
					if (Send("top " + string(buf) + " 1\r\n")) {
						string ret = Receive(-1);
						if (ret.length() > 0 && ret[0] == '+') {
							Email().FillTopInfo(ret).PrintTopInfo();
						}
						else throw Exception(ret);
					}
					else throw Exception("send top failed");

				}
			}
			else throw Exception(ret);
		}
		else throw Exception("send list failed");
	}
	void Retr(int id) {
		char buf[20];
		sprintf(buf, "%d", id);
		if (Send("retr " + string(buf) + "\r\n")) {
			printf("receiving...\n");
			string ret = Receive(-1);
			if (ret.length() > 0 && ret[0] == '+') {
				printf("receive success!\n");
				Email().FillAll(ret).PrintTopInfo().Save();
			}
			else throw Exception(ret);
		}
		else throw Exception("send retr failed");
	}
	~POP3Connector() {

	}
};

class SMTPConnector : public TCPConnector {
	string emailAddress;
	string password;
	static const int port = 25;
	string SMTPClientDomain;
public:
	SMTPConnector(string emailAddress, string password) :TCPConnector(GetEmailServerIP("smtp", emailAddress), port) {
		this->SMTPClientDomain = "myemail-client";
		this->emailAddress = emailAddress;
		this->password = password;
		Receive(100); //
		if (Send("ehlo " + SMTPClientDomain + "\r\n")) {
			string ret = Receive(200);
			if (ret.length() > 0 && ret.substr(0, 3) == "250") {
				AuthLogin();
			}
			else throw Exception(ret);
		}
		else throw Exception("send ehlo failed");
	}
	bool SendEmail(string destEmailAddress, const string & content) {
		if (Send("mail from: <" + emailAddress + ">\r\n")) {
			string ret = Receive(50);
			if (ret.length() > 0 && ret.substr(0, 3) == "250") {
				if (Send("rcpt to: <" + destEmailAddress + ">\r\n")) {
					ret = Receive(50);
					if (ret.length() > 0 && ret.substr(0, 3) == "250") {
						if (Send("data\r\n")) {
							ret = Receive(50);
							if (ret.length() > 0 && ret.substr(0, 3) == "354") {
								printf("sending...\n");
								if (Send(content)) {
									ret = Receive(200);
									if (ret.length() > 0 && ret.substr(0, 3) == "250") { return true; }
									else throw Exception(ret);
								}
								else throw Exception("send content failed");
							}
							else throw Exception(ret);
						}
						else throw Exception("send data failed");
					}
					else throw Exception(ret);
				}
				else throw Exception("rcpt to failed");
			}
			else throw Exception(ret);
		}
		else throw Exception("mail from failed");
		return false;
	}
private:
	void AuthLogin() {
		if (Send("auth login\r\n")) {
			string ret = Receive(50);
			if (ret.length() > 0 && ret.substr(0, 3) == "334") {
				string encodeStr;
				FILE* temp = fopen("temp", "w");
				string user = GetUserFromEmailAddress(emailAddress);
				fwrite(user.c_str(), 1, user.length(), temp);
				fclose(temp);
				EnCode(encodeStr, "temp", "Base64");
				if (Send(encodeStr + "\r\n")) {
					ret = Receive(50);
					if (ret.length() > 0 && ret.substr(0, 3) == "334") {
						temp = fopen("temp", "w");
						fwrite(password.c_str(), 1, password.length(), temp);
						fclose(temp);
						EnCode(encodeStr, "temp", "Base64");
						if (Send(encodeStr + "\r\n")) {
							printf("auth login success!\n");
							ret = Receive(50);
							if (!(ret.length() > 0 && ret.substr(0, 3) == "235"))
								throw Exception(ret);
						}
						else throw Exception("send password failed in auth login");
					}
					else throw Exception("a2" + ret);
				}
				else throw Exception("send user failed in auth login");
			}
			else throw Exception("a3" + ret);
		}
		else throw Exception("send auth login failed");
	}
};
/*
	myemail bind username@xxx.xx password

	myemail bind status

	myemail list [count]

	myemail retr id

	myemail send username@xxxx.xx contentfilename [utf-8] [attachmentfilename]
*/
string PATH;
void SaveInfoToFile(string emailAddress, string password) {
	FILE* fp = fopen((PATH + "\\myemail.conf").c_str(), "w");
	if (fp != NULL)
		fprintf(fp, "%s\n%s", emailAddress.c_str(), password.c_str());
	else throw Exception("can't create myemail.conf in " + PATH + "; maybe you don't have permission");
	fclose(fp);
}
bool GetInfoFromFile(string& emailAddress, string& password) {
	char addr[100];
	char pass[100];
	FILE* fp = fopen((PATH + "\\myemail.conf").c_str(), "r");
	if (fp != NULL) {
		fscanf(fp, "%s\n%s", addr, pass);
		fclose(fp);
		emailAddress = addr;
		password = pass;
		return true;
	}
	else return false;
}
//222114
bool isNumber(string s) {
	if (s.length() == 0) return false;
	for (int i = 0; i < s.length(); i++) {
		if (s[i] < '0' || s[i]>'9') return false;
	}
	return true;
}
int main(int argc, char* argv[]) {
	char buf[100];
	GetModuleFileNameA(NULL, buf, 100);
	string t = string(buf);
	PATH = t.substr(0, t.rfind('\\'));
	WSADATA wsaData;
	int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup failed: %d\n", iResult);
		return 1;
	}
	if (argc == 4 && !strcmp(argv[1], "bind")) {
		try {
			POP3Connector pop3(argv[2], argv[3]);
			SaveInfoToFile(argv[2], argv[3]);
			printf("binding success!\n");
		}
		catch (Exception e) {
			e.Print();
		}
	}
	else if (argc == 3 && !strcmp(argv[2], "status")) {
		string addr;
		string pass;
		if (GetInfoFromFile(addr, pass)) {
			printf("Eamil Address: %s\nPassword: %s\n", addr.c_str(), pass.c_str());
		}
		else printf("you did not bind before\n");
	}
	else if ((argc == 2 || argc == 3) && !strcmp(argv[1], "list")) {
		try {
			string addr;
			string pass;
			if (GetInfoFromFile(addr, pass)) {
				POP3Connector pop3(addr, pass);
				if (argc == 2) pop3.List();
				else {
					int t;
					if (isNumber(argv[2])) {
						sscanf(argv[2], "%d", &t);
						pop3.List(t);
					}
					else printf("please input a number(>-1) after list\n");
				}
			}
			else printf("you did not bind before\n");
		}
		catch (Exception e) {
			e.Print();
		}
	}
	else if (argc == 3 && !strcmp(argv[1], "retr")) {
		try {
			string addr;
			string pass;
			if (GetInfoFromFile(addr, pass)) {
				POP3Connector pop3(addr, pass);
				int id = 0;
				if (isNumber(argv[2])) {
					sscanf(argv[2], "%d", &id);
					pop3.Retr(id);
				}
				else printf("please input a number(>-1) after retr\n");
			}
			else printf("you did not bind before\n");
		}
		catch (Exception e) {
			e.Print();
		}
	}
	else if ((argc == 4 || argc == 5 || argc == 6) && !strcmp(argv[1], "send")) {
		try {
			string addr;
			string pass;
			//myemail send username@xxxx.xx contentfilename [utf-8] [attachmentfilename]
			if (GetInfoFromFile(addr, pass)) {

				string charset = "gb18030";
				MIMEContent cont("multipart/alternative;", "8Bit");
				if (argc == 4) {
					cont.AddMIMEContent(new MIMEContent("text/plain; charset=\"" + charset + "\";", "base64", argv[3]));
				}
				else if (argc > 4 && string(argv[4]) == "utf-8") {
					charset = "utf-8";
					cont.AddMIMEContent(new MIMEContent("text/plain; charset=\"" + charset + "\";", "base64", argv[3]));
					if (argc == 6) {
						cont.AddMIMEContent(new MIMEContent("application/octet-stream;",
							"base64", argv[5], "attachment;"));
					}
				}
				else if (argc == 5) {
					cont.AddMIMEContent(new MIMEContent("text/plain; charset=\"" + charset + "\";", "base64", argv[3]));
					cont.AddMIMEContent(new MIMEContent("application/octet-stream;",
						"base64", argv[4], "attachment;"));
				}
				else throw Exception("only can attach one attachment");

				string content = Email().SetFrom(addr).SetTo(argv[2]).SetSubject(argv[3])
					.SetMIMEContent(&cont)
					.BuidEmailString();

				SMTPConnector smtp(addr, pass);
				if (!smtp.SendEmail(argv[2], content)) {
					throw Exception("send email failed");
				}
				else printf("send success!\n");

			}
			else printf("you did not bind before\n");
		}
		catch (Exception e) {
			e.Print();
		}
	}
	else {
		printf("unable to parse command\n");
		WSACleanup();
	}
	return 0;
}